home *** CD-ROM | disk | FTP | other *** search
/ BMUG PD-ROM A / PD-ROM A.iso / Programming / Programming Languages / MacOberon / MacOberon (tools) / EditKeys.Mod (.txt) < prev    next >
Encoding:
Oberon Text  |  1991-02-15  |  15.7 KB  |  416 lines  |  [.Ob./.Ob2]

  1. Syntax10.Scn.Fnt
  2. Syntax10i.Scn.Fnt
  3. Syntax10b.Scn.Fnt
  4. MODULE EditKeys;    (* CAS 2-Nov-90 *)
  5.     (* Please Ed.Open EditKeys.Tool *)
  6.     IMPORT
  7.         Oberon, Input, Fonts, Display, Viewers, Texts, TextFrames, MenuViewers;
  8.     CONST
  9.         IdentLen = 32; DefaultFile = "EditKeys.Text"; DefaultMacro = "OTHERWISE";
  10.         Menu = "System.Close  System.Copy  System.Grow  Edit.Search  Edit.Store";
  11.         (*scanner symbols*)
  12.             cmdSym = 0; nameSym = 1; stretchSym = 2; lparen = 3; rparen = 4; bslash = 5; eof = 6;
  13.         (*built-in commands; first w/o, then w/ param*)
  14.             writeCmd = "0"; charCmd = "1"; fntCmd = "2"; callCmd = "4";
  15.             keepCmd = "5"; pickCmd = "6"; caretCmd = "7"; indentCmd = "8";
  16.         (*preset.set*)
  17.             fntPreset = 0; pickPreset = 1; caretPreset = 3;
  18.     TYPE
  19.         Ident = ARRAY IdentLen OF CHAR;
  20.         Definition = POINTER TO DefinitionDesc;
  21.         Sequence = POINTER TO SequenceDesc;
  22.         DefinitionDesc = RECORD
  23.             left, right: Definition;
  24.             in: BOOLEAN;
  25.             trig: Ident;
  26.             seq: Sequence
  27.         END;
  28.         SequenceDesc = RECORD
  29.             next: Sequence;
  30.             sym: INTEGER;
  31.             cmd: CHAR;
  32.             def: Definition;
  33.             stretch: Texts.Buffer
  34.         END;
  35.         defs: Definition;
  36.         ch, cmd, hotKey: CHAR;
  37.         sym, errs: INTEGER;
  38.         errpos: LONGINT;
  39.         name, trig: Ident;
  40.         stretch, buf, indent: Texts.Buffer;
  41.         T: Texts.Text;
  42.         R: Texts.Reader;
  43.         W, WB, WL: Texts.Writer;    (*out, compose, Leda*)
  44.         preset: RECORD
  45.             set: SET;
  46.             pos, caret: LONGINT;
  47.             frame: TextFrames.Frame;
  48.             def, fnt: Fonts.Font
  49.         END;
  50.     PROCEDURE Flip(VAR src, dst: ARRAY OF CHAR);
  51.         VAR i, j: INTEGER;
  52.     BEGIN i := -1; j := 0;
  53.         REPEAT INC(i) UNTIL src[i] = 0X;
  54.         WHILE i > 0 DO DEC(i); dst[j] := src[i]; INC(j) END;
  55.         dst[j] := 0X
  56.     END Flip;
  57.     PROCEDURE WLog;
  58.     BEGIN Texts.Append(Oberon.Log, W.buf)
  59.     END WLog;
  60.     PROCEDURE Ch(ch: CHAR);
  61.     BEGIN Texts.Write(W, ch)
  62.     END Ch;
  63.     PROCEDURE Str(s: ARRAY OF CHAR);
  64.     BEGIN Texts.WriteString(W, s)
  65.     END Str;
  66.     PROCEDURE FlipStr(s: ARRAY OF CHAR);
  67.         VAR n: Ident;
  68.     BEGIN Flip(s, n); Str(n)
  69.     END FlipStr;
  70.     PROCEDURE Gap;
  71.     BEGIN Str("  ")
  72.     END Gap;
  73.     PROCEDURE Ln;
  74.     BEGIN Texts.WriteLn(W)
  75.     END Ln;
  76.     PROCEDURE Char(ch: CHAR);
  77.         VAR i, n: INTEGER; d: ARRAY 3 OF CHAR;
  78.     BEGIN
  79.         IF (ch < " ") OR (ch >= 7FX) THEN
  80.             i := 0; n := ORD(ch); REPEAT d[i] := CHR(n MOD 10 + 30H); n := n DIV 10; INC(i) UNTIL n = 0;
  81.             Ch("#"); WHILE i > 0 DO DEC(i); Ch(d[i]) END
  82.         ELSE Ch(ch)
  83.         END
  84.     END Char;
  85.     PROCEDURE Append(SB, DB: Texts.Buffer);
  86.     BEGIN Texts.Copy(SB, DB); Texts.OpenBuf(SB)
  87.     END Append;
  88.     (* table handler *)
  89.     PROCEDURE ResetDefs;
  90.     BEGIN defs.right := NIL; defs.trig[0] := 0X; hotKey := "\"
  91.     END ResetDefs;
  92.     PROCEDURE Find(VAR name, trig: Ident; insert: BOOLEAN): Definition;
  93.         VAR p, q, d, x: Definition;    i, j: INTEGER;
  94.     BEGIN Flip(name, trig); p := defs; d := p.right; q := NIL; x := NIL;
  95.         WHILE d # NIL DO i := 0;
  96.             WHILE (trig[i] # 0X) & (trig[i] = d.trig[i]) DO INC(i) END;
  97.             IF trig[i] = d.trig[i] THEN x := d; d := NIL
  98.             ELSIF trig[i] = 0X THEN q := d; d := NIL
  99.             ELSIF trig[i] < d.trig[i] THEN p := d; d := d.left
  100.             ELSE p := d; d := d.right
  101.             END
  102.         END;
  103.         IF insert & (x = NIL) THEN NEW(x); x.right := q; x.in := FALSE; x.trig := trig;
  104.             IF (q # NIL) & (q.left # NIL) & (q.left.trig < trig) THEN x.left := q.left; q.left := NIL ELSE x.left := NIL END;
  105.             IF trig < p.trig THEN p.left := x ELSE p.right := x END
  106.         END;
  107.         RETURN x
  108.     END Find;
  109.     PROCEDURE Trigger(VAR trig: Ident): Definition;
  110.         VAR d, x: Definition;    i: INTEGER;
  111.     BEGIN d := defs.right; x := NIL;
  112.         WHILE d # NIL DO i := 0;
  113.             WHILE (trig[i] # 0X) & (trig[i] = d.trig[i]) DO INC(i) END;
  114.             IF trig[i] = d.trig[i] THEN RETURN d END;
  115.             IF d.trig[i] = 0X THEN x := d END;
  116.             IF trig[i] < d.trig[i] THEN d := d.left ELSE d := d.right END
  117.         END;
  118.         RETURN x
  119.     END Trigger;
  120.     (* macro compiler *)
  121.     PROCEDURE Mark(err: ARRAY OF CHAR);
  122.     BEGIN INC(errs);
  123.         IF Texts.Pos(R) - errpos > 9 THEN
  124.             errpos := Texts.Pos(R); Ln; Str("  pos  "); Texts.WriteInt(W, errpos, 0); Gap; Str(err); WLog
  125.         END
  126.     END Mark;
  127.     PROCEDURE GetCh;
  128.     BEGIN Texts.Read(R, ch)
  129.     END GetCh;
  130.     PROCEDURE CharCode;
  131.         VAR c: INTEGER;
  132.     BEGIN c := 0;
  133.         WHILE ("0" <= ch) & (ch <= "9") DO c := c * 10 + SHORT(ORD(ch) - 30H); GetCh END;
  134.         name[0] := CHR(c); name[1] := 0X    (*unchecked*)
  135.     END CharCode;
  136.     PROCEDURE Name;
  137.         VAR i: INTEGER;
  138.     BEGIN i := 0;
  139.         REPEAT name[i] := ch; INC(i); GetCh
  140.         UNTIL (ch <= " ") OR (ch = 22X) OR (ch = "#") OR (ch = "(") OR (ch = ")") OR (ch = "\") OR (ch = "^")
  141.             OR R.eot OR (i = IdentLen-1);
  142.         name[i] := 0X
  143.     END Name;
  144.     PROCEDURE Stretch;
  145.         VAR beg, end: LONGINT;
  146.     BEGIN beg := Texts.Pos(R); end := beg; GetCh;
  147.         WHILE ~R.eot & (ch # 22X) DO INC(end); GetCh END;
  148.         IF ch = 22X THEN GetCh;
  149.             IF end > beg THEN NEW(stretch); Texts.OpenBuf(stretch); Texts.Save(T, beg, end, stretch)
  150.             ELSE Mark("empty stretch")
  151.             END
  152.         ELSE Mark("Closing quote expected")
  153.         END
  154.     END Stretch;
  155.     PROCEDURE Comment;
  156.     BEGIN
  157.         LOOP GetCh;
  158.             IF ch = "(" THEN GetCh;
  159.                 IF ch = "*" THEN GetCh; Comment END
  160.             ELSIF ch = "*" THEN GetCh;
  161.                 IF ch = ")" THEN GetCh; EXIT END
  162.             END
  163.         END
  164.     END Comment;
  165.     PROCEDURE GetSym;
  166.     BEGIN sym := eof;
  167.         REPEAT
  168.             IF (0X <= ch) & (ch <= " ") THEN GetCh
  169.             ELSIF ch = 22X THEN sym := stretchSym; Stretch
  170.             ELSIF ch = "#" THEN sym := nameSym; GetCh; CharCode
  171.             ELSIF ch = "(" THEN GetCh; IF ch = "*" THEN GetCh; Comment ELSE sym := lparen END
  172.             ELSIF ch = ")" THEN sym := rparen; GetCh
  173.             ELSIF ch = "\" THEN sym := bslash; GetCh
  174.             ELSIF ch = "^" THEN sym := cmdSym; GetCh; cmd := ch; GetCh
  175.             ELSE sym := nameSym; Name
  176.             END
  177.         UNTIL (sym # eof) OR R.eot
  178.     END GetSym;
  179.     PROCEDURE ParseText;
  180.         VAR def: Definition;    beg, seq: Sequence;    f: BOOLEAN;
  181.     BEGIN GetSym;
  182.         IF sym = bslash THEN
  183.             GetSym; IF sym = nameSym THEN hotKey := name[0]; GetSym ELSE Mark("hot-key code expected") END
  184.         END;
  185.         WHILE sym = nameSym DO GetSym; def := Find(name, trig, TRUE);
  186.             IF sym = lparen THEN
  187.                 GetSym; NEW(beg); seq := beg; beg.next := NIL;
  188.                 WHILE sym IN {cmdSym, nameSym, stretchSym} DO
  189.                     NEW(seq.next); seq := seq.next; seq.sym := sym;
  190.                     IF sym = cmdSym THEN seq.cmd := cmd
  191.                     ELSIF sym = nameSym THEN seq.def := Find(name, trig, FALSE);
  192.                         IF seq.def = NIL THEN Mark("illegal forward reference") END
  193.                     ELSE (*sym = stretchSym*) seq.stretch := stretch
  194.                     END;
  195.                     GetSym
  196.                 END;
  197.                 def.seq := beg.next; IF sym = rparen THEN GetSym ELSE Mark(") expected") END
  198.             ELSE Mark("( expected")
  199.             END
  200.         END;
  201.         IF sym # eof THEN GetSym;
  202.             IF sym # eof THEN Mark("unexpected trailing char.s") END
  203.         END;
  204.         stretch := NIL
  205.     END ParseText;
  206.     PROCEDURE ReadFile(name: ARRAY OF CHAR);
  207.     BEGIN T := TextFrames.Text(name); Texts.OpenReader(R, T, 0); GetCh;
  208.         IF ~R.eot THEN Str("  reading "); Str(name); WLog; errs := 0; errpos := -10; ParseText;
  209.             IF errs = 0 THEN Str(" done") ELSE ResetDefs END;
  210.             Ln; WLog
  211.         END
  212.     END ReadFile;
  213.     (* macro processor *)
  214.     PROCEDURE SetCaret(frame: TextFrames.Frame; pos: LONGINT);
  215.     BEGIN
  216.         IF frame.car > 0 THEN TextFrames.RemoveCaret(frame) END;
  217.         TextFrames.SetCaret(frame, pos)
  218.     END SetCaret;
  219.     PROCEDURE Insert(frame: TextFrames.Frame; buf: Texts.Buffer);
  220.         VAR pos, len: LONGINT;
  221.     BEGIN pos := frame.carloc.pos; len := buf.len;
  222.         Texts.Insert(frame.text, pos, buf); SetCaret(frame, pos + len)
  223.     END Insert;
  224.     PROCEDURE Delete(frame: TextFrames.Frame; len: LONGINT);
  225.         VAR pos: LONGINT;
  226.     BEGIN pos := frame.carloc.pos;
  227.         Texts.Delete(frame.text, pos - len, pos); SetCaret(frame, pos - len)
  228.     END Delete;
  229.     PROCEDURE Err(def: Definition; s: ARRAY OF CHAR);
  230.     BEGIN INC(errs); Gap; Str(s); Str(" in "); FlipStr(def.trig); Ln; WLog
  231.     END Err;
  232.     PROCEDURE Arg(def: Definition; class: SHORTINT; VAR S: Texts.Scanner; VAR stack: Sequence);
  233.         VAR B: Texts.Buffer;
  234.     BEGIN
  235.         IF stack # NIL THEN T := TextFrames.Text(""); NEW(B); Texts.OpenBuf(B); Texts.Copy(stack.stretch, B);
  236.             Texts.Append(T, B); Texts.OpenScanner(S, T, 0); Texts.Scan(S); stack := stack.next;
  237.             IF S.class # class THEN Err(def, "illegal param type") END
  238.         ELSE Err(def, "missing param")
  239.         END
  240.     END Arg;
  241.     PROCEDURE Expand(def: Definition; VAR stack: Sequence);
  242.         VAR seq, u: Sequence;    S: Texts.Scanner;    par: Oberon.ParList;    res: INTEGER;
  243.     BEGIN
  244.         IF ~def.in THEN def.in := TRUE; seq := def.seq;
  245.             WHILE seq # NIL DO
  246.                 IF seq.sym = cmdSym THEN
  247.                     IF seq.cmd = writeCmd THEN
  248.                         IF stack # NIL THEN Texts.Copy(stack.stretch, buf); stack := stack.next
  249.                         ELSE Err(def, "missing param")
  250.                         END
  251.                     ELSIF seq.cmd = charCmd THEN Arg(def, Texts.Int, S, stack);
  252.                         Texts.Write(WB, CHR(S.i)); Append(WB.buf, buf)
  253.                     ELSIF seq.cmd = fntCmd THEN Arg(def, Texts.Name, S, stack);
  254.                         INCL(preset.set, fntPreset); preset.fnt := Fonts.This(S.s)
  255.                     ELSIF seq.cmd = callCmd THEN Arg(def, Texts.Name, S, stack);
  256.                         IF errs = 0 THEN NEW(par); par.vwr := Oberon.FocusViewer; par.frame := par.vwr.dsc.next;
  257.                             par.text := T; par.pos := Texts.Pos(S); Oberon.Call(S.s, par, FALSE, res);
  258.                             IF res # 0 THEN Texts.WriteInt(W, res, 3); Err(def, "call error ") END
  259.                         END
  260.                     ELSIF seq.cmd = keepCmd THEN INCL(preset.set, fntPreset); preset.fnt := preset.def
  261.                     ELSIF seq.cmd = pickCmd THEN INCL(preset.set, pickPreset)
  262.                     ELSIF seq.cmd = caretCmd THEN INCL(preset.set, caretPreset); preset.caret := buf.len
  263.                     ELSIF seq.cmd = indentCmd THEN
  264.                         IF indent.len > 0 THEN Texts.Copy(indent, buf) END
  265.                     ELSE Err(def, "illegal built-in")
  266.                     END
  267.                 ELSIF seq.sym = nameSym THEN Expand(seq.def, stack)
  268.                 ELSE (*seq.sym = stretchSym*) NEW(u); u.next := stack; stack := u;
  269.                     NEW(u.stretch); Texts.OpenBuf(u.stretch); Texts.Copy(seq.stretch, u.stretch)
  270.                 END;
  271.                 seq := seq.next
  272.             END;
  273.             def.in := FALSE
  274.         ELSE Err(def, "cyclic expansion")
  275.         END
  276.     END Expand;
  277.     PROCEDURE Process(frame: TextFrames.Frame; ch: CHAR; VAR del: LONGINT);
  278.         VAR def: Definition;    pos, i: LONGINT;    stack: Sequence;    default: BOOLEAN;
  279.     BEGIN errs := 0; del := 0; Texts.OpenBuf(buf); pos := frame.carloc.pos - (IdentLen-1);
  280.         IF pos >= 0 THEN i := IdentLen-1 ELSE pos := 0; i := frame.carloc.pos END;
  281.         trig[i] := 0X; Texts.OpenReader(R, frame.text, pos); REPEAT DEC(i); Texts.Read(R, trig[i]) UNTIL i = 0;
  282.         def := Trigger(trig); default := def = NIL;
  283.         IF default THEN name := DefaultMacro; def := Find(name, trig, FALSE) END;
  284.         IF def # NIL THEN preset.set := {}; stack := NIL; errs := 0; Expand(def, stack);
  285.             IF stack # NIL THEN Err(def, "superfluous param") END;
  286.             IF errs = 0 THEN
  287.                 IF ~default THEN
  288.                     REPEAT INC(del) UNTIL def.trig[del] = 0X
  289.                 END;
  290.                 preset.frame := frame; preset.pos := frame.carloc.pos - del;
  291.                 IF caretPreset IN preset.set THEN INC(preset.pos, preset.caret) ELSE INC(preset.pos, buf.len) END
  292.             END
  293.         END
  294.     END Process;
  295.     (* editor interface *)
  296.     PROCEDURE Key(frame: TextFrames.Frame; ch: CHAR; VAR handled: BOOLEAN);
  297.         CONST BS = 1X; TAB = 9X; LF = 0AX; CR = 0DX; DEL = 7FX; RtArrow = 0C3X; LtArrow = 0C4X;
  298.         VAR del, pos, beg: LONGINT; i: INTEGER;    fnt: Fonts.Font;    ch1: CHAR;
  299.     BEGIN handled := TRUE; pos := frame.carloc.pos;
  300.         IF pos > 0 THEN Texts.OpenReader(R, frame.text, pos - 1); Texts.Read(R, ch1); preset.def := R.fnt
  301.         ELSE preset.def := Fonts.Default
  302.         END;
  303.         IF (preset.frame = frame) & (preset.pos = pos) & (fntPreset IN preset.set) THEN fnt := preset.fnt
  304.         ELSE fnt := preset.def
  305.         END;
  306.         preset.set := {}; preset.frame := NIL;
  307.         IF WL.fnt # fnt THEN Texts.SetFont(WL, fnt) END;
  308.         IF ch = hotKey THEN beg := frame.carloc.org; Texts.OpenReader(R, frame.text, beg); Texts.Read(R, ch1);
  309.             WHILE (Texts.Pos(R) <= pos) & (ch1 <= " ") DO Texts.Read(R, ch1) END;
  310.             Texts.OpenBuf(indent); IF beg < Texts.Pos(R) - 1 THEN Texts.Save(frame.text, beg, Texts.Pos(R) - 1, indent) END;
  311.             Process(frame, ch, del);
  312.             IF errs = 0 THEN
  313.                 IF (frame.car > 0) & (frame.carloc.pos # pos) THEN preset.pos := frame.carloc.pos - del END;
  314.                 SetCaret(frame, pos);
  315.                 IF del > 0 THEN Delete(frame, del) END;
  316.                 IF pickPreset IN preset.set THEN T := TextFrames.Text(""); Texts.Append(T, buf);
  317.                     Texts.ChangeLooks(T, 0, T.len, {0}, preset.def, 0, 0); Texts.Save(T, 0, T.len, buf)
  318.                 END;
  319.                 Insert(frame, buf);
  320.                 IF (0 <= preset.pos) & (preset.pos <= frame.text.len) THEN SetCaret(frame, preset.pos) END
  321.             END
  322.         ELSIF (ch = BS) OR (ch = TAB) OR (ch = LF) OR (ch = CR) OR (ch = DEL) OR (ch = RtArrow) OR (ch = LtArrow) THEN
  323.             handled := FALSE
  324.         ELSE Texts.Write(WL, ch)
  325.         END;
  326.         IF (WL.buf.len > 0) & (frame.car > 0) THEN Insert(frame, WL.buf) END
  327.     END Key;
  328.     PROCEDURE* Handler(F: Display.Frame; VAR msg: Display.FrameMsg);
  329.         CONST InvalMsg = -1;
  330.         VAR frame: TextFrames.Frame; handled: BOOLEAN;
  331.     BEGIN
  332.         IF msg IS Oberon.InputMsg THEN
  333.             WITH msg: Oberon.InputMsg DO frame := F(TextFrames.Frame);
  334.                 IF (msg.id = Oberon.consume) & (frame.car > 0) THEN Key(frame, msg.ch, handled);
  335.                     IF handled THEN msg.id := InvalMsg END
  336.                 ELSIF (msg.id = Oberon.track) & (msg.keys # {}) THEN preset.set := {}
  337.                 END
  338.             END
  339.         END
  340.     END Handler;
  341.     PROCEDURE GetHandler*;
  342.         CONST Magic = -42;
  343.     BEGIN
  344.         IF Oberon.Par.pos = Magic THEN Oberon.Par.frame.handle := Handler END
  345.     END GetHandler;
  346.     (* commands *)
  347.     PROCEDURE Reset*;
  348.     BEGIN ResetDefs
  349.     END Reset;
  350.     PROCEDURE ReadFiles*;
  351.         VAR S: Texts.Scanner;
  352.     BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S);
  353.         WHILE S.class = Texts.Name DO ReadFile(S.s); Texts.Scan(S) END;
  354.         T := NIL
  355.     END ReadFiles;
  356.     PROCEDURE Definitions*;
  357.         VAR
  358.             S: Texts.Scanner;    V: MenuViewers.Viewer;    text: Texts.Text;    tree: Definition;
  359.             beg, end, time: LONGINT;    x, y: INTEGER;
  360.         PROCEDURE Sort(VAR d: Definition; VAR pat: ARRAY OF CHAR; VAR tree: Definition);
  361.             VAR i: INTEGER;    t: Definition;    n: Ident;
  362.             PROCEDURE Ins(VAR tree: Definition; t: Definition; VAR n: Ident);
  363.                 VAR m: Ident;
  364.             BEGIN
  365.                 IF tree = NIL THEN tree := t
  366.                 ELSE Flip(tree.trig, m);
  367.                     IF n < m THEN Ins(tree.left, t, n) ELSE Ins(tree.right, t, n) END
  368.                 END
  369.             END Ins;
  370.         BEGIN
  371.             IF d # NIL THEN Flip(d.trig, n); i := 0;
  372.                 WHILE (pat[i] # 0X) & (n[i] = pat[i]) DO INC(i) END;
  373.                 IF pat[i] = 0X THEN NEW(t); t^ := d^; t.left := NIL; t.right := NIL; Ins(tree, t, n) END;
  374.                 Sort(d.left, pat, tree); Sort(d.right, pat, tree)
  375.             END
  376.         END Sort;
  377.         PROCEDURE Write(tree: Definition);
  378.             PROCEDURE Seq(seq: Sequence);
  379.             BEGIN
  380.                 WHILE seq # NIL DO
  381.                     IF seq.sym = cmdSym THEN Ch("^"); Ch(seq.cmd)
  382.                     ELSIF seq.sym = nameSym THEN
  383.                         IF (name[1] = 0X) & ((name[0] < " ") OR (name[0] > 7EX)) THEN Char(name[0])
  384.                         ELSE FlipStr(seq.def.trig)
  385.                         END
  386.                     ELSE (*seq.sym = stretchSym*) Ch(22X); Append(W.buf, buf); Texts.Copy(seq.stretch, buf); Ch(22X)
  387.                     END;
  388.                     seq := seq.next; IF seq # NIL THEN Ch(" ") END
  389.                 END
  390.             END Seq;
  391.         BEGIN
  392.             IF tree # NIL THEN
  393.                 Write(tree.left); Gap; FlipStr(tree.trig); Gap; Ch("("); Seq(tree.seq); Ch(")"); Ln; Write(tree.right)
  394.             END
  395.         END Write;
  396.     BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S);
  397.         IF (S.line # 0) OR (S.class # Texts.String) & (S.class # Texts.Name) THEN
  398.             Oberon.GetSelection(text, beg, end, time);
  399.             IF time > 0 THEN Texts.OpenScanner(S, text, beg); Texts.Scan(S) END
  400.         END;
  401.         IF (S.line # 0) OR (S.class # Texts.String) & (S.class # Texts.Name) THEN S.s[0] := 0X END;
  402.         Str("(hotkey is "); Char(hotKey); Ch(")"); Ln;
  403.         tree := NIL; Sort(defs.right, S.s, tree); Texts.OpenBuf(buf); Write(tree); Append(W.buf, buf);
  404.         Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y); text := TextFrames.Text(""); Texts.Append(text, buf);
  405.         V := MenuViewers.New(TextFrames.NewMenu("EditKeys.Definitions", Menu), 
  406.             TextFrames.NewText(text, 0), TextFrames.menuH, x, y)
  407.     END Definitions;
  408.     PROCEDURE GetKeyCode*;
  409.     BEGIN Str("EditKeys.KeyCode  ('q' to quit)"); WLog;
  410.         REPEAT Input.Read(ch); Ln; Gap; Char(ch); WLog UNTIL ch = "q";
  411.         Ln; WLog
  412.     END GetKeyCode;
  413. BEGIN Texts.OpenWriter(W); Texts.OpenWriter(WB); Texts.OpenWriter(WL); NEW(defs); NEW(buf); NEW(indent);
  414.     ResetDefs; ReadFile(DefaultFile)
  415. END EditKeys.
  416.